package loop

import (
	"github.com/garyburd/redigo/redis"
	"libs"
	"log"
	"strings"
	"time"
	"fmt"
	"config"
)

var monitorStats = make(map[string]int)

/*
订阅KEY过期事件，把消费超时的数据重新放回队列
 */
func Subscribe(event string) {
	psc := redis.PubSubConn{Conn: libs.RedisPool.Get()}
	psc.Subscribe(event)
	for {
		switch v := psc.Receive().(type) {
		case redis.Message:
			//log.Printf("%s: message: %s\n", v.Channel, v.Data)
			delHashKey(string(v.Data))
		case redis.Subscription:
			log.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
		case error:
			log.Println("redis subscript error: ", v.Error())
			time.Sleep(1 * time.Second)
			psc.Close()
			psc = redis.PubSubConn{Conn: libs.RedisPool.Get()}
			psc.Subscribe(event)
		}
	}
}

/*
重回队列的逻辑：
1. 通过过期KEY的名字（token:consumer_id:topic）获取队列名称（consumer_id:topic）
2. 通过hash表（consumer_id:topic:__tmp__）获取该次消费的数据，并从hash表中删除该数据
3. 把数据放回队列
 */
func delHashKey(key string) {
	defer func() {
		if err := recover(); err != nil {
			log.Println("del hash key panic: ", err)
		}
	}()
	fields := strings.SplitN(key, ":", 2)
	if len(fields) < 2 {
		return
	}
	qName := fields[1]
	hashKey := qName + ":__tmp__"
	commands := []*libs.MultiLine {
		libs.NewMultiLine("HMGET", hashKey, fields[0]),
		libs.NewMultiLine("HDEL", hashKey, fields[0]),
	}
	reply, err := redis.Values(libs.MultiExec(commands))
	if err != nil {
		log.Println(err)
		return
	}
	value := make([]string, 0)
	if _, err := redis.Scan(reply, &value); err != nil {
		log.Println(err)
		return
	}
	if len(value) == 1 {
		if err := libs.RPush(qName, value[0]); err != nil {
			log.Println(err)
		}
	}
}

/*
检测队列堆积情况，分别在60% 80% 100%是告警
 */
func Monitor() {
	for {
		time.Sleep(2 * time.Second)
		for _, topic := range libs.TopicMap.All() {
			if count, err := libs.LLen(topic); err != nil {
				log.Println(err)
			} else if count > config.MaxTopicCount {
				if err := libs.LTrim(topic, config.MaxTopicCount-config.MaxTopicCount*2, -1); err != nil {
					log.Println(err)
				}
				if monitorStats[topic] < 100 {
					monitorStats[topic] = 100
					libs.CallDingWebHook("队列堆积告警", fmt.Sprintf(
						"*队列堆积告警*\n- 队列名称：%s\n- 告警阈值：%d\n- 当前数值：%d\n- 告警说明：已超过上限，启用自动修剪",
						topic,
						config.MaxTopicCount,
						count,
					))
				}
			} else if count >= config.MaxTopicCount*0.8 && monitorStats[topic] < 80 {
				monitorStats[topic] = 80
				libs.CallDingWebHook("队列堆积告警", fmt.Sprintf(
					"*队列堆积告警*\n- 队列名称：%s\n- 告警阈值：%d\n- 当前数值：%d\n- 告警说明：已达到上限80%%",
					topic,
					config.MaxTopicCount,
					count,
				))
			} else if count >= config.MaxTopicCount*0.6 && monitorStats[topic] < 60 {
				monitorStats[topic] = 60
				libs.CallDingWebHook("队列堆积告警", fmt.Sprintf(
					"*队列堆积告警*\n- 队列名称：%s\n- 告警阈值：%d\n- 当前数值：%d\n- 告警说明：已达到上限60%%",
					topic,
					config.MaxTopicCount,
					count,
				))
			} else if count <= config.MaxTopicCount*0.5 && monitorStats[topic] != 0 {
				monitorStats[topic] = 0
				libs.CallDingWebHook("队列堆积恢复", fmt.Sprintf(
					"*队列堆积恢复*\n- 队列名称：%s\n- 告警阈值：%d\n- 当前数值：%d\n- 告警说明：恢复正常范围",
					topic,
					config.MaxTopicCount,
					count,
				))
			}
		}
	}
}
